Data processing systems include data storage devices for storing, processing, and moving data. A memory device, a hard disk drive, and an optical or tape drive are some examples of data storage devices. A data storage device is generally a physical component of a data processing system configured to store data.
A data storage device may also include logical or virtual components. For example, a memory may also include a space on a hard disk designated to be used as a virtual part of the memory.
In order to use data stored on a data storage device, an application has to request that data. For example, an application may read data from a hard disk using an I/O request of a certain type. The application may write that data to a memory location using a different I/O request. The application may update the data in the hard disk by writing updated data to the hard disk using another type of I/O request.
Certain components in a data processing system may also communicate with each other using I/O requests. For example one component may read or update another component's data in memory using I/O requests.
Often, in a data processing system, the component that receives an I/O request from an application does not actually perform the data input/output. A component that receives an I/O request passes the request to another component for processing. In many cases, an I/O request may pass through a series of components before reaching a component that can access the data for the operation being requested in the I/O request. Such a series of components is called a “stack.” A stack is a logical hierarchical arrangement of components where a first component may be a child in a parent-child relationship with a second component, and a parent in another parent-child relationship with a third component. A parent component can have any number of children components in a stack.
An I/O request may fail to process as desired for a variety of reasons. For example, the processing of an I/O request may exceed a time threshold set for the processing, and consequently, the I/O request may fail due to a timeout. As another example, a component in a stack that may be handling the I/O request may go offline or become non-responsive, causing the I/O request to stall or hang, to wit, remain pending with the component for an indeterminate period.
As another example, a parent component in a given stack may accept the I/O request and pass the request down to a child component. The child component may recognize that the I/O request is attempting to perform an unauthorized or illegal operation, or is attempting to access prohibited data, and may stop processing the I/O request.
A component detecting a problem with an I/O request need not explicitly cancel the I/O request. Presently, an error handling mechanism in the component fails the I/O request upon detecting a problem with the I/O request. However, a process for recovering from an I/O request that has failed, or has violated a condition, may include cancelling the I/O request. For example, once a component determines that an I/O request has stalled or failed, the component may begin a cancellation process implemented for that component.
When a component cancels an I/O request, subsequent actions with respect to the I/O request, such as retrying the I/O request, or conclusively failing the I/O request and returning an error to the requesting application, are contingent upon the completion of the I/O request cancellation process. For example, an I/O request may have to wait to be retried until the information indicating a cancellation of a pending I/O request has propagated from the component that cancelled the request all the way up the stack to the root component in the stack.